#ifdef __APPLE__
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#include <GLUT/glut.h>
#else
#define GL_GLEXT_PROTOTYPES
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
#endif

#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <math.h>
#define ESCAPE 27

int window;

typedef struct
{
	float x,y;
}Punto;

typedef struct
{
	float r,g,b;
}Color;

GLint modelPosUniform;
GLint modelRotUniform;
GLint modelScaleUniform;

GLuint loadShaderFile(GLint shadertype, const char * filename)
{
	int f = open( filename, O_RDONLY );     // abrimos el archivo
	off_t fileLength = lseek(f,0,SEEK_END); // tomamos su tamaño
	lseek(f,0,SEEK_SET);                    // volvemos al principio del archivo
	char * fileText = (char*) malloc(sizeof(char)*fileLength); // creamos un buffer para almacenar el texto
	read(f, fileText, fileLength);          // leemos el texto
	close( f);

	GLuint shader = glCreateShader(shadertype); //GL_FRAGMENT_SHADER o GL_VERTEX_SHADER
	glShaderSource(shader,1,(const GLchar **)&fileText,NULL);

	// Lo compilamos 
	glCompileShader(shader);

	GLint shaderCompiledOK;
	glGetShaderiv(shader,GL_COMPILE_STATUS,&shaderCompiledOK);
	if(!shaderCompiledOK)
	{
		int logSize;
		glGetShaderiv(shader,GL_INFO_LOG_LENGTH,&logSize);
		char * log = (char*)malloc(sizeof(char)*logSize+1);
		glGetShaderInfoLog(shader, logSize, &logSize, log);
		printf("Log de compilado: %s\n",log);
		free(log);
	}
	return shader;
}

void InitGL (int windowWidth, int windowHeight, int windowDepth, const char * windowTitle)
{
	glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH);
	glutInitWindowSize(windowWidth,windowHeight);
	window = glutCreateWindow(windowTitle);

	// Codigo de los shaders
	GLuint vshader = loadShaderFile(GL_VERTEX_SHADER,"shader.vert");
    GLuint fshader= loadShaderFile(GL_FRAGMENT_SHADER,"shader.frag");

	// Creamos el shader program y unimos los shaders
	GLuint program = glCreateProgram();
	glAttachShader(program, vshader);
	glAttachShader(program, fshader);

	// Enlazamos el programa
	glLinkProgram(program);

	// Indicamos que queremos usar este shader
	glUseProgram(program);
	modelPosUniform = glGetUniformLocation(	program, "modelpos");
	modelRotUniform = glGetUniformLocation(	program, "modelrot");
	modelScaleUniform = glGetUniformLocation( program, "modelScale");
}

void keyPressed(unsigned char key, int x, int y) 
{
	if (key == ESCAPE) 
	{ 
		glutDestroyWindow(window); 
	    exit(1);
    }
}

void DrawGLScene()
{	
	static float time = 0;
	time +=0.001;

	// Limpiamos la imagen con color blanco
	glClearColor(1.0,1.0,1.0,1.0);
	glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);

	// Coordenadas de los puntos del objeto
	const Punto vertexPos[3] = { {-0.25,-0.15}, {0.25,-0.15}, {0,0.35} };
	const Color vertexColor[9] = { {0.0,0.0,1.0}, {0.0,1.0,0.0}, {1.0,0.0,0.0} };

	// Activamos el render de vertex y color arrays
	glEnableClientState(GL_VERTEX_ARRAY);
	glEnableClientState(GL_COLOR_ARRAY);

	// Indicamos de qué arrays se deben tomar los datos
	glColorPointer(3,GL_FLOAT,0,vertexColor);
	glVertexPointer(2,GL_FLOAT,0,vertexPos);

	// Posicion de los diferentes objetos
	const Punto modelspos[5] = { {0,0}, {0.4,-0.4}, {-0.4,-0.4}, {0.4,-0.4}, {-0.4,-0.4} };

	for(int i=0;i<5;++i)
	{
		// Enviamos las coordenadas del modelo a la GPU
		glUniform2fv(modelPosUniform,2,&(modelspos[i].x));
		glUniform1f(modelRotUniform,time);
		glUniform1f(modelScaleUniform,sin(time)*0.5 + 0.6);
		// Dibujamos el triangulo
		glDrawArrays(GL_TRIANGLES,0, 3);
	}
	// Mostramos el triangulo en pantalla 
	glutSwapBuffers();
}

void ReSizeGLScene(int width, int height)
{
	glViewport(0, 0, width, height);		
}

int main(int argc, char ** argv) 
{
	glutInit(&argc, argv);  
	InitGL(800,600,24,"OpenGL Tutorial1");
	glutDisplayFunc(&DrawGLScene);  

	glutIdleFunc(&DrawGLScene);
	glutReshapeFunc(&ReSizeGLScene);
	glutKeyboardFunc(&keyPressed);

	glutMainLoop();  
	return 1;
}

